﻿using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;

using WiiDeviceLibrary;

namespace com.ReinforceLab.AlphaRexNxtStep
{
    class WiiBoardConnection : IDisposable, System.ComponentModel.INotifyPropertyChanged
    {
        #region Variables
        readonly ApplicationContext context;

        bool _is_connected;
        
        float _bottomRightWeight;
        float _topRightWeight;
        float _bottomLeftWeight;
        float _topLeftWeight;

        IBalanceBoard   board;
        IDeviceProvider provider;

        public EventHandler<EventArgs> BoardStateUpdated;
        #endregion

        #region Porperties
        public IBalanceBoard Board { get { return board; } }

        public const String PN_IsConnected = "IsConnected";
        public bool IsConnected {
            get { return _is_connected; }
            private set { _is_connected = value; invokePropertyChanged(PN_IsConnected); }
        }

        public const String PN_BottomRightWeight = "BottomRightWeight";
        public float BottomRightWeight { 
            get { return _bottomRightWeight; }
            private set { _bottomRightWeight = value; invokePropertyChanged(PN_BottomRightWeight); }
        }

        public const String PN_TopRightWeight = "TopRightWeight";
        public float TopRightWeight {
            get { return _topRightWeight; }
            private set { _topRightWeight = value; invokePropertyChanged(PN_TopRightWeight); }
        }

        public const String PN_BottomLeftWeight = "BottomLeftWeight";
        public float BottomLeftWeight {
            get { return _bottomLeftWeight; }
            private set { _bottomLeftWeight = value; invokePropertyChanged(PN_BottomLeftWeight); }
        }

        public const String PN_TopLeftWeight = "TopLeftWeight";
        public float TopLeftWeight {
            get { return _topLeftWeight; }
            private set { _topLeftWeight = value; invokePropertyChanged(PN_TopLeftWeight); }
        }
        #endregion

        #region Constructor
        public WiiBoardConnection( ApplicationContext ctx)
        {
            context = ctx;
            initialize();
        }
        void initialize()
        {
            board = null;
            IsConnected = false;
            updateBoardValues();

            initProvider();
        }
        #endregion

        #region Private methods
        void initProvider()
        {
            if (null != provider)
            {
                provider.DeviceFound -= new EventHandler<DeviceInfoEventArgs>(_provider_DeviceFound);
                provider.DeviceLost  -= new EventHandler<DeviceInfoEventArgs>(_provider_DeviceLost);

                if (provider.IsDiscovering)
                    provider.StopDiscovering();
            }

            provider = DeviceProviderRegistry.CreateSupportedDeviceProvider();
            provider.DeviceFound += new EventHandler<DeviceInfoEventArgs>(_provider_DeviceFound);
            provider.DeviceLost += new EventHandler<DeviceInfoEventArgs>(_provider_DeviceLost);
            provider.StartDiscovering();
        }
        void _provider_DeviceLost(object sender, DeviceInfoEventArgs e)
        {
            context.WriteConsole("BT device lost.");
        }
        void _provider_DeviceFound(object sender, DeviceInfoEventArgs e)
        {
            context.WriteConsole("BT device found.");

            if (null != board)
                return;
            // auto connect
            try
            {
                IDevice device = provider.Connect(e.DeviceInfo);
                if (null != device)
                {
                    if (device is IBalanceBoard)
                    {
                        board = device as IBalanceBoard;
                        board.Updated += board_Updated;
                        board.Disconnected += board_Disconnected;

                        IsConnected = true;
                        context.WriteConsole("BalanceBoard is connected.");
                    }
                    else
                    {
                        device.Disconnect();

                        context.WriteConsole("This is NOT BalanceBoard. Disconnected.");
                    }
                }
            }
            catch (Exception ex)
            {
                context.WriteConsole("Exception when connecting to a wii board.");
                showExceptionDialog(ex.Message);
                initProvider();
            }
        }
        void showExceptionDialog(String message)
        {
            System.Windows.MessageBox.Show("Wii balance board connection failed.\n" + message, "Fatal", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
        }
        void updateBoardValues()
        {
            if (null == board)
            {
                TopLeftWeight  = 0f;
                TopRightWeight = 0f;
                BottomLeftWeight  = 0f;
                BottomRightWeight = 0f;
            }
            else
            {
                TopLeftWeight  = board.TopLeftWeight;
                TopRightWeight = board.TopRightWeight;
                BottomLeftWeight  = board.BottomLeftWeight;
                BottomRightWeight = board.BottomRightWeight;
            }
        }
        #endregion

        #region Event handlers
        void board_Updated(object sender, EventArgs e)
        {
            updateBoardValues();

            if (null != BoardStateUpdated)
                BoardStateUpdated.Invoke(this, null);                
        }
        void board_Disconnected(object sender, EventArgs e)
        {
            IsConnected = false;

            board.Updated      -= board_Updated;
            board.Disconnected -= board_Disconnected;
            board = null;

            context.WriteConsole("BalanceBoard disconnected event.");
        }       
        #endregion

        #region IDisposable メンバ
        public void Dispose()
        {
            if (null != board)
            {
                IsConnected = false;

                board.Updated      -=board_Updated;
                board.Disconnected -=board_Disconnected;
                board.Disconnect();
                board = null;
            }

            if (null != provider)
            {
                provider.DeviceFound -= new EventHandler<DeviceInfoEventArgs>(_provider_DeviceFound);
                provider.DeviceLost -= new EventHandler<DeviceInfoEventArgs>(_provider_DeviceLost);
                provider.StopDiscovering();
                provider = null;
            }

            context.WriteConsole("Wii board connection is disposed.");
        }
        #endregion

        #region INotifyPropertyChanged メンバ
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        void invokePropertyChanged(String name)
        {
            if (null != PropertyChanged)
                PropertyChanged.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name));
        }
        #endregion
    }
}
